package com.bqjr.cloud.zuul.dao;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public abstract class BaseDao<T> {
	
	@Autowired
	protected JdbcTemplate JdbcTemplate;
	protected Class<T> entityClass;
	protected String tableName;
	
	public BaseDao() {
		Class<?> clazz = getClass();
		Type type = clazz.getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
			entityClass = (Class) parameterizedType[0];
			Table table = entityClass.getAnnotation(Table.class);
			tableName = table.name();
		}
	}
	
	public void insert(T entity) {
		List<String> fieldNameList = new ArrayList<String>();
		List<Object> fieldValueList = new ArrayList<Object>();
		ParseHelper.parseObject(entity, fieldNameList, fieldValueList);
		String fieldNames = ParseHelper.parseList(fieldNameList, ",");
		StringBuilder fieldValues = new StringBuilder();
		for (int i = 0, size = fieldValueList.size(); i < size; i++) {
			fieldValues.append("?,");
		}
		fieldValues.deleteCharAt(fieldValues.length() - 1);
		StringBuilder sql = new StringBuilder();
		sql.append("insert into ").append(tableName).append("(").append(fieldNames).append(") values(").append(fieldValues.toString()).append(")");
		JdbcTemplate.update(sql.toString(), fieldValueList.toArray());
	}

	public void update(T entity) {
		List<String> fieldNameList = new ArrayList<String>();
		List<Object> fieldValueList = new ArrayList<Object>();
		String key = ParseHelper.parseObject(entity, fieldNameList, fieldValueList);
		StringBuilder keyClause = new StringBuilder();
		StringBuilder setBuilders = new StringBuilder();
		for (int i = 0, size = fieldNameList.size(); i < size; i++) {
			String fieldName = fieldNameList.get(i);
			if (fieldName.equals(key)) {
				keyClause.append(key + "=?");
				fieldValueList.add(fieldValueList.remove(i));
			} else {
				setBuilders.append(fieldName + "=?,");
			}
		}
		setBuilders.deleteCharAt(setBuilders.length() - 1);
		StringBuilder sql = new StringBuilder();
		sql.append("update ").append(tableName).append(" set ").append(setBuilders.toString()).append(" where ").append(keyClause);
		JdbcTemplate.update(sql.toString(), fieldValueList.toArray());
	}

	public void delete(String field, Object value) {
		StringBuilder sql = new StringBuilder();
		sql.append("delete from ").append(tableName).append(" where ").append(field).append("=?");
		JdbcTemplate.update(sql.toString(), value);
	}
	
	public boolean exist(String field, Object value) {
		if (get(field, value) != null) {
			return true;
		}
		return false;
	}
	
	public T get(String field, Object value) {
		List<T> list = getList(field, value);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}
	
	public List<T> getList(String field, Object value) {
		StringBuilder sql = new StringBuilder();
		sql.append("select * from ").append(tableName).append(" where ").append(field).append("=?");
		return JdbcTemplate.query(sql.toString(), new BeanPropertyRowMapper<T>(entityClass), value);
	}
	
	public List<T> getAll() {
		StringBuilder sql = new StringBuilder();
		sql.append("select * from ").append(tableName);
		return JdbcTemplate.query(sql.toString(), new BeanPropertyRowMapper<T>(entityClass));
	}
	
	public List<T> queryList(String sql, Object... args) {
		return JdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(entityClass), args);
	}
	
	public int queryCount(String sql, Object... args) {
		return JdbcTemplate.queryForObject(sql, Integer.class, args);
	}
	
	
	/////////////////////////////////////////内部解析辅助类///////////////////////////////////////////
	static class ParseHelper {

		static String parseObject(Object obj, List<String> fieldNameList, List<Object> fieldValueList) {
			String key = null;
			try {
				Class<?> superClazz = obj.getClass().getSuperclass();
				while (superClazz != null) {
					key = parseField(obj, fieldNameList, fieldValueList, superClazz);
					superClazz = superClazz.getSuperclass();
				}
				key = parseField(obj, fieldNameList, fieldValueList, obj.getClass());
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			return key;
		}
		
		static String parseField(Object obj, List<String> fieldNameList, List<Object> fieldValueList, Class<?> clazz) throws Exception {
			String key = null;
			if (clazz != Object.class) {
				String fieldName = null;
				Field[] clazzFields = clazz.getDeclaredFields();
				for (Field field : clazzFields) {
					if (field.getAnnotation(Transient.class) == null && !Modifier.isStatic(field.getModifiers())) {
						Class<?> fieldType = field.getType();
						if (fieldType.isPrimitive() || fieldType == Boolean.class || fieldType == String.class 
								|| fieldType == Integer.class || fieldType == Long.class || fieldType == Float.class 
								|| fieldType == Double.class || fieldType == BigDecimal.class|| fieldType == Date.class 
								|| fieldType.getSuperclass() == Enum.class) {
							fieldName = field.getName();
							if (field.getAnnotation(Id.class) != null) {
								key = fieldName;
							}
							if (fieldValueList != null) {
								field.setAccessible(true);
								Object fieldValue = field.get(obj);
								if (fieldValue != null) {
									if (fieldType.getSuperclass() == Enum.class) {
										fieldValue = fieldValue.toString();
									}
									fieldNameList.add(fieldName);
									fieldValueList.add(fieldValue);
								}
							} else {
								fieldNameList.add(fieldName);
							}
						}
					}
				}
			}
			return key;
		}
		
		static String parseList(List<?> list, String splitStr) {
			if (list == null || list.size() == 0) {
				return "";
			}
			StringBuilder sb = new StringBuilder();
			for (Object obj : list) {
				sb.append(obj + splitStr);
			}
			sb.deleteCharAt(sb.length()-1);
			return sb.toString();
		}
		
	}
	

}
